// Constants
static final int COST_BARRICADE = 0;

// Object instances
Player player;
ActorManager am;

// Global variables
boolean horizontal = false;
public int state = 0;
int money = 100;
int difficulty = 1;
boolean spawn = true;
public boolean waveFinished = false;
public boolean waveStarted = false;

void setup()
{
 size(displayWidth - 100, displayHeight - 100);
 
 // Initiate object instance
 am = new ActorManager();
 player = new Player(width/2, height - 64);
 
}

void draw()
{
  background(0);
  textSize(30);
  fill(0, 255, 0);
  text("$"+ money, width/2 - 10, 50);
  
  // If in build phase
  if(state == 0)
  {
      waveStarted = false;
      spawn = true;
      text("Build Phase", 50, 50);
    
      //Display next wave button
      rect(10, displayHeight - 200, 40, 20);
   
      // Render marker for barricade placement
      stroke(255);
      fill(255, 0);
      if(!horizontal)
      {
      //Vertical
      rect(mouseX, mouseY, 16, 32);
      }
      else
      {
      //Horizontal
      rect(mouseX, mouseY, 32, 16);
      }
         
  }
  // If in combat phase
  else if(state == 1)
  {
    text("Combat Phase", 50, 50);
    
    //display continue button after wave
    if(waveFinished)
     rect(10, displayHeight - 200, 40, 20);
    
    if(spawn)
    {
      for(int i = 0; i <= difficulty; ++i)
      {
      am.addEnemy(0);
      }
      waveStarted = true;
    spawn = false;
    ++difficulty;
    }
    
    
    
    player.Render();
    player.Update();
  }
  
  // Handle
  am.Render();
  am.Update();
}                     

void mousePressed(){

    
    if(money > 0 && state == 0)
    {
      if(mouseX > 50)
      {
        am.addPlaceable(horizontal, mouseX, mouseY);
        money -= COST_BARRICADE;
      }
    }
    
    //Input combat
    if(state == 1)
    {
       am.addProjectile(mouseX+29, height-64); 
       
       // If continue button is pressed
       if(mouseX < 50 && mouseX > 10 &&
       mouseY < displayHeight - 200 + 20 && mouseY > displayHeight - 200)
       {
         if(mousePressed && waveFinished)
         {
           state = 0;
         }      
       }
    }
    //Input build
    else if(state == 0)
    {
      // If next wave button pressed
      if(mouseX < 50 && mouseX > 10 &&
         mouseY < displayHeight - 200 + 20 && mouseY > displayHeight - 200)
         {
           if(mousePressed)
           {
             state = 1;
           }      
         }
    }
}


// as long as you click and drag, it will give the box a new position
void mouseDragged()
{

}

// if you let go of a mouse button, it's not locked to mouse position
void mouseReleased()
{
  
}


void keyReleased()
  {
    // Barricade rotation toogle
    if (key == 'r' || key == 'R')
    {
      if(horizontal)
      {
        horizontal = false;
      }
      else if(!horizontal)
      {
        horizontal = true;
      }
    }
    //Toogle phase
    else if( key == 't' || key == 'T')
    {
       if(state == 0)
        state = 1;
       else if(state == 1)
        state = 0; 
    }
  }


class ActorManager
{
  // Constants
  static final int PULSE_SPEED = 3;
  static final int COLLISION_BOX = 50;
  static final float ENEMY_DMG = 0.1f;
  static final float PLAYER_DMG = 10;
  
  ArrayList<Enemy> enemies;
  ArrayList<Placeable> placeables;
  ArrayList<Projectile> projectiles;
  PImage barver;
  PImage barhoriz;
  PImage barver_pulse;
  PImage barhoriz_pulse;
  PImage enemyTexture;
  int pulseAlpha = 255;
  boolean pulse = false;
  
  ActorManager()
  {
    enemies = new ArrayList<Enemy>();
    placeables = new ArrayList<Placeable>();
    projectiles = new ArrayList<Projectile>();
    barhoriz = loadImage("barricade.png");
    barver = loadImage("barricade1.png");
    barhoriz_pulse = loadImage("barricade_pulse.png");
    barver_pulse = loadImage("barricade1_pulse.png");
    enemyTexture = loadImage("enemy.png");
  }
  
  
  void addEnemy(float y)
  {
    enemies.add(new Enemy(y));
  }
  
  void addProjectile(float x, float y)
  {
    projectiles.add(new Projectile(x, y)); 
  }
  
  void addPlaceable(boolean rot, float x, float y)
  {
    placeables.add(new Placeable(rot, x, y));
  }
  
  void Render()
  {
    //Render projectiles
    for(int i = 0; i < projectiles.size(); ++i)
    {
      Projectile projectile = projectiles.get(i);
     
      rect(projectile.getX(), projectile.getY(), 1, 10);
    }
    
    //Render placeables
    for(int i = 0; i < placeables.size(); ++i)
    {
      Placeable placeable = placeables.get(i);
      
      if(placeable.getBarHz())
      {
        tint(255, placeable.getAlpha());
        image(barhoriz, placeable.getX(), placeable.getY());
        tint(255, pulseAlpha);
        image(barhoriz_pulse, placeable.getX(), placeable.getY());
      }
      else
      {
        tint(255, placeable.getAlpha());
        image(barver, placeable.getX(), placeable.getY());
        tint(255, pulseAlpha);
        image(barver_pulse, placeable.getX(), placeable.getY());
      }
      
      fill(255, 0, 0);
      textSize(32);
      //text("Barricades placed: " + placeables.size(), 400, 50);
      textSize(10);
      //text("Health:" + placeable.getHealth(), placeable.getX(), placeable.getY()+ 45);
      
      
    }
    //Render enemies
    for(int i = 0; i < enemies.size(); ++i)
    {
      Enemy enemy = enemies.get(i);
      
      // Render enemy
      //rect(enemy.getX(), enemy.getY(), 20, 20);
      tint(255,255);
      image(enemyTexture, enemy.getX(), enemy.getY());
      
      //Render enemy health count
      textSize(12);
      fill(255);
      //text("health"+enemy.getHealth(), enemy.getX(), enemy.getY());
      
      // Render enemy count
      fill(255, 0, 0);
      textSize(32);
      //text("Enemies: " + enemies.size(), 200, 50);
    }
  }
  
  void Update()
  {
    //Handle state
    if(waveFinished)
    {
      waveFinished = false;
    }
    if(enemies.size() == 0 && waveStarted)
    {
      waveFinished = true;
    }
    
    // Handle projectiles
    for(int i = 0; i < projectiles.size(); ++i)
    {
      Projectile projectile = projectiles.get(i);
      
      // Update
      projectile.Update();
      
      // Check collision with enemies
      for (int j = 0; j < enemies.size(); ++j)
      {
         Enemy enemy = enemies.get(j);
         
         if(projectile.getX() > enemy.getX() && projectile.getX() < enemy.getX() + COLLISION_BOX
           && projectile.getY() > enemy.getY() && projectile.getY() < enemy.getY() + COLLISION_BOX)
         {
           enemy.looseHealth(PLAYER_DMG);
         }
      }
    }
    
    //Handle enemies
    for(int i = 0; i < enemies.size(); ++i)
    {
      Enemy enemy = enemies.get(i);
      int count = placeables.size();
      int index = int(random(0, count));
      
      if(enemy.getY() > height)
      {
        money -= 1;
        enemies.remove(i);
      }
      
      // Give target if there are barricades present
      if(!enemy.hasTarget && placeables.size() > 0)
      {
        enemy.setTargetX(placeables.get(index).getX());
        enemy.setTargetY(placeables.get(index).getY());
        enemy.setHasTarget(true);
      }
      else if(placeables.size() <= 0)
      {
        enemy.setTargetY(height);
        enemy.setTargetX(random(0, width));
      }
      
        enemy.Update();
        if(enemy.getHealth() <= 0)
        {
          enemies.remove(i);
        }
    }   
    // handle placeables
    for(int i = 0; i < placeables.size(); ++i)
    {
      Placeable placeable = placeables.get(i);
      placeable.Update();
      
      //enemy collision with placeable
      for(int j = 0; j < enemies.size(); ++j)
      {
        Enemy enemy = enemies.get(j);
        if(enemy.getX() < placeable.getX() + COLLISION_BOX && enemy.getX() > placeable.getX() - COLLISION_BOX
           && enemy.getY() > placeable.getY() - COLLISION_BOX && enemy.getY() < placeable.getY() + COLLISION_BOX)
        {
          placeable.looseHealth(ENEMY_DMG);
        }
      }
      //Check if target is existing
      for(int j = 0; j < enemies.size(); ++j)
      {
        Enemy enemy = enemies.get(j);
        if(enemy.getX() < placeable.getX() + COLLISION_BOX && enemy.getX() > placeable.getX() - COLLISION_BOX
           && enemy.getY() > placeable.getY() - COLLISION_BOX && enemy.getY() < placeable.getY() + COLLISION_BOX)
        {
          if(placeable.getHealth() <= 0)
          {
            enemy.setHasTarget(false);
          }
        }
      }

      
      //remove if destroyed
    if(placeable.getDeadTimer() <= 0)
      {
        placeables.remove(i);
      }
    }
    
    
    // Adjust barricade pulse
    // Flag for pulsation rhytm
    if (pulseAlpha >= 255)
    {
       pulse = false;
    }
    else if (pulseAlpha <= 30)
    {
       pulse = true;
    }
    
    // Increase light if pulse is flagged
    if(pulse)
      pulseAlpha += PULSE_SPEED;
    // Decrease
    else if(!pulse)
      pulseAlpha -= PULSE_SPEED;
    
  }
}
class Enemy
{
  //Constants
  static final int SPEED_LIMIT = 3;
  
  float px;
  float py;
  float velocityX;
  float velocityY;
  float targetX = 0;
  float targetY = 0;
  boolean hasTarget = false;
  int health = 100;
  
  Enemy(float y)
  {
    px = random(100, width - 100);
    py = y;
    
    // Set start velocity
    velocityX = 0;
    velocityY = 0;
    
    // Set default target
    targetX = random(100, width- 100);
    targetY = height;
  }
  
  float getX()
  {
    return px;
  }
  
  float getY()
  {
    return py;
  }
  
  void setTargetY(float y)
  {
    targetY = y;
  }
  
  void setTargetX(float x)
  {
    targetX = x;
  }
  
  float getTargetX()
  {
    return targetX;
  }
  
  float getTargetY()
  {
    return targetY;
  }
  
  boolean hasTarget()
  {
    return hasTarget;
  }
  
  int getHealth()
  {
    return health;
  }
  
  void setHasTarget(boolean target)
  {
    hasTarget = target;
  }
  
  void looseHealth(float damage)
  {
     health -= damage; 
  }
  
  void Update()
  {
    
    //SEEK N DESTROY
    if(targetX != 0 && targetY != 0)
    {
      if(px < targetX)
      {
        ++velocityX;
      }
      else if(px > targetX)
      {
        --velocityX;
      }
      if(py < targetY)
      {
        ++velocityY;
      }
      else if(py > targetY)
      {
        --velocityY;
      }
    }
    
    //speedl imitsor
    
    if(velocityX > SPEED_LIMIT)
    {
      velocityX = SPEED_LIMIT;
    }
    if(velocityY > SPEED_LIMIT)
    {
      velocityY = SPEED_LIMIT;
    }
    
    if(velocityX < -SPEED_LIMIT)
    {
      velocityX = -SPEED_LIMIT;
    }
    if(velocityY < -SPEED_LIMIT)
    {
      velocityY = -SPEED_LIMIT;
    }
    
    //APPLY VELOCITY
    px += velocityX;
    py += velocityY;
    
  }



}
class Placeable
{
  static final float DEAD_TIMER_SPEED = 1;
  float deadTimer = 100;
  boolean barHz;
  float px;
  float py;
  float health = 150;
  int alpha = 255;
  
  Placeable(boolean new_barHz, float x, float y)
  {
    
    px = x;
    py = y;
    barHz = new_barHz;
  }
  
  float getX()
  {
    return px;
  }
  
  float getY()
  {
    return py;
  }
  
  boolean getBarHz()
  {
    return barHz;
  }
  
  float getHealth()
  {
    return health;
  }
  
  void looseHealth(float dmg)
  {
    health -= dmg;
  }
  float getDeadTimer()
  {
    return deadTimer;
  }
  
  int getAlpha()
  {
   return alpha; 
  }
  
  void Update()
  {
  
    if(health <= 0)
    {
      deadTimer -= DEAD_TIMER_SPEED;
      alpha -= 2;
    }
    
  }
  
}
class Player
{
  float playerX;
  float playerY;
  PImage player;
  
  Player(float x, float y)
  {
     playerX = x;
     playerY = y;
     player = loadImage("player.png");
  }
  
  void Render()
  {
    //rect(mouseX, playerY, 30.0f, 30.0f);
    image(player, mouseX, playerY);
  }
  
  void Update()
  {
    //Handle input
    
  }
  
  float getX()
  {
     return playerX; 
  }
  
  float getY()
  {
     return playerY; 
  }
}
class Projectile
{
  //constants
  static final float PROJECTILE_SPEED = 20.0f;
  
  float projectileX;
  float projectileY;
   
  Projectile(float x, float y)
  {
    projectileX = x;
    projectileY = y;
  }
 
  void Update()
  {
    projectileY -= PROJECTILE_SPEED; 
  }
  
  float getX()
  {
   return projectileX; 
  }
  
  float getY()
  {
   return projectileY; 
  }
}

